
export function formatDate(date, fmt) {
    if (/(y+)/.test(fmt)) {
        fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length))
    }
    let o = {
        'M+': date.getMonth() + 1,
        'd+': date.getDate(),
        'h+': date.getHours(),
        'm+': date.getMinutes(),
        's+': date.getSeconds(),
    }
    for (let k in o) {
        if (new RegExp(`(${k})`).test(fmt)) {
            let str = o[k] + ''
            fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? str : padLeftZero(str))
        }
    }
    return fmt
}

export function parseDate(str, fmt) {

    if (str instanceof Date) {
        return str;
    }

    var data = {};
    var year, month, date;

    if (/(y+)/.test(fmt)) {
        data.setFullYear = parseInt(str.substr(fmt.indexOf(RegExp.$1), RegExp.$1.length));
    }

    let o = {
        'M+': "setMonth",
        'd+': "setDate",
        'h+': "setHours",
        'm+': "setMinutes",
        's+': "setSeconds",
    }

    for (let k in o) {
        if (new RegExp(`(${k})`).test(fmt)) {

            data[o[k]] = parseInt(str.substr(fmt.indexOf(RegExp.$1), RegExp.$1.length));

            if (k.indexOf("M") != -1) {
                data[o[k]] = data[o[k]] - 1;
            }
        }
    }

    var d = new Date(0);
    d.setHours(0);

    for (let key in data) {
        d[key](data[key]);
    }

    return d;
}

function padLeftZero(str, prev) {
    prev = prev || "00";
    return (prev + str).substr(str.length)
}

export function arrayMerge(arr1, arr2) {
    var _arr = new Array();
    for (var i = 0; i < arr1.length; i++) {
        _arr.push(arr1[i]);
    }
    for (var i = 0; i < arr2.length; i++) {
        var flag = true;
        for (var j = 0; j < arr1.length; j++) {
            if (arr2[i] == arr1[j]) {
                flag = false;
                break;
            }
        }
        if (flag) {
            _arr.push(arr2[i]);
        }
    }
    return _arr;
}

export function arrayDiff(array1, array2) {
    array1 = array1.map(x => JSON.stringify(x));
    array2 = array2.map(x => JSON.stringify(x));
    var __arr = new Array();
    for (let i in array1) {
        if (array2.indexOf(array1[i]) > -1)
            continue;
        __arr.push(JSON.parse(array1[i]))
    }
    return __arr;
}

/**
 * 对象合并，和 Object.assign 不同的是，这是利用JSON进行的脱引用合并。
 * 比 Object.assign 好的是，合并后是全新的变量（除了function外），
 * 不会使用之前变量的内存引用。
 *
 * @return {[type]}         [description]
 */
export function objectAssign() {
    var args = Array.from( arguments);
    // var args = Array.prototype.slice.call(null, arguments, 0);
    args.forEach((item, i) => {
        if (item == null) item = {};
        args[i] = JSON.parse(JSON.stringify(item));
        args[i] = Object.assign(args[i], item);
    });
    return Object.assign.apply(null, args);
}


/**
 * 从给定的对象里，返回一个只包含指定属性名的对象。
 *
 * @param   {[type]}  obj               源对象
 * @param   {[type]}  fields            新对象的属性名，可用英文逗号分隔
 * @param   {[type]}  containUndefined  如果指定属性在源对象中不存在(undefined)，是否也要设置进新对象中。
 *
 * @return  {[type]}                    [return description]
 */
export function filterObjectAttr(obj, fields, containUndefined) {
    obj = objectAssign({}, obj);
    var _obj = {};
    if (typeof fields == "string") fields = fields.split(",");

    fields.forEach((key) => {
        if (obj[key] !== undefined || !!containUndefined) {
            _obj[key] = obj[key];
        }
    });
    return _obj;
}


/**
 * 用于从不同对象中提取指定的属性，然后组成一个新的对象。
 * 该方法综合使用 filterObjectAttr 和 objectAssign 两个函数来实现这一功能。
 * 第一个参数就是属性名，可为以英文逗号分隔的字符串，或者直接填字符串数组。
 *
 * @return {[type]} [description]
 */
export function objectAssignWithKeys() {
    // var args = Array.prototype.slice.call(null, arguments, 0);
    var args = Array.from( arguments);
    var fields = args.shift();
    if (typeof fields == "string") fields = fields.split(",");

    var ret = objectAssign.apply(null, args);
    return filterObjectAttr(ret, fields);
}

/**
 * 基于 Object.assign ，实现深层次对象合并
 *
 * @param   {[type]}  target   [target description]
 * @param   {[type]}  sources  [sources description]
 *
 * @return  {[type]}           [return description]
 */
export function assignDeep() {
  let args = Array.from(arguments);
  args = args || [];
  let target = args.shift();
  let sources = args.concat([]);
  sources = sources || [];

  // 1. 参数校验
  if (target == null) {
    throw new TypeError("Cannot convert undefined or null to object");
  }

  // 2. 如果是基本类型，则转换包装对象
  let result = new Object(target);
  // 3. 缓存已拷贝过的对象
  let hash = new WeakMap();

  // 4. 目标属性是否可直接覆盖赋值判断
  function canPropertyCover(node) {
    if (!node.target[node.key]) {
      return true;
    }
    if (node.target[node.key] == null) {
      return true;
    }
    if (!(typeof node.target[node.key] === "object")) {
      return true;
    }
    if (Array.isArray(node.target[node.key]) !== Array.isArray(node.data)) {
      return true;
    }
    return false;
  }

  sources.forEach(v => {
    let source = new Object(v);

    let stack = [
      {
        data: source,
        key: undefined,
        target: result
      }
    ];

    while (stack.length > 0) {
      let node = stack.pop();
      if (typeof node.data === "object" && node.data !== null) {
        let isPropertyDone = false;
        if (hash.get(node.data) && node.key !== undefined) {
          if (canPropertyCover(node)) {
            node.target[node.key] = hash.get(node.data);
            isPropertyDone = true;
          }
        }

        if (!isPropertyDone) {
          let target;
          if (node.key !== undefined) {
            if (canPropertyCover(node)) {
              target = Array.isArray(node.data) ? [] : {};
              hash.set(node.data, target);
              node.target[node.key] = target;
            } else {
              target = node.target[node.key];
            }
          } else {
            target = node.target;
          }

          Reflect.ownKeys(node.data).forEach(key => {
            // 过滤不可枚举属性
            if (!Object.getOwnPropertyDescriptor(node.data, key).enumerable) {
              return;
            }
            stack.push({
              data: node.data[key],
              key: key,
              target: target
            });
          });
        }
      } else {
        Object.assign(node.target, { [node.key]: node.data });
      }
    }
  });

  return result;
}
